package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.apache.commons.lang3.ObjectUtils.max;
import static org.apache.commons.lang3.ObjectUtils.min;

@Aspect
@Component
public class ApiProfilerAspect {


    //调用次数统计结果表
    final Map<String,Integer> invokeTimes= Collections.synchronizedMap(new HashMap<>());

    //异常次数统计结果表
    final Map<String,Integer> errorTimes= Collections.synchronizedMap(new HashMap<>());

    //统计各个API的最长响应时间
    final Map<String,Long> longestTime= Collections.synchronizedMap(new HashMap<>());

    // 最短响应时间
    final Map<String,Long> shortestTime= Collections.synchronizedMap(new HashMap<>());

    // 平均响应时间
    final Map<String,Long> avgTime= Collections.synchronizedMap(new HashMap<>());

    //统计异常次数
    @AfterThrowing(value = "@within(org.springframework.stereotype.Service)",throwing = "ex")
    public void calculateError(JoinPoint joinPoint,Exception ex){
        String methodSig = joinPoint.getSignature().toString();
        Integer base = errorTimes.containsKey(methodSig)?errorTimes.get(methodSig):0;
        errorTimes.put(methodSig,base+1);
    }



    //统计各个API的最长响应时间、最短响应时间、平均响应时间
    @Around("@within(org.springframework.stereotype.Service)")
    public void calculateTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodSig=joinPoint.getSignature().toString();
        try{
            long t1= Calendar.getInstance().getTimeInMillis();
            joinPoint.proceed();
            long t2= Calendar.getInstance().getTimeInMillis();


            //统计各个API的最长响应时间
            long base= longestTime.containsKey(methodSig)?longestTime.get(methodSig):0;
            longestTime.put(methodSig,max(t2-t1,base));

            // 最短响应时间
            base = shortestTime.containsKey(methodSig)?shortestTime.get(methodSig):99999;
            shortestTime.put(methodSig,min(base,t2-t1));

            //平均响应时间
            Integer invokeTime = invokeTimes.get(methodSig);
            base = avgTime.containsKey(methodSig)?avgTime.get(methodSig):0;
            avgTime.put(methodSig,(base*(invokeTime-1)+(t2-t1))/(invokeTime));

        }catch (Exception e){
            String exceptionName = e.getClass().getName();
            Integer base = errorTimes.get(exceptionName);
            errorTimes.put(exceptionName,base==null?1:base+1);
        }

    }



    //统计调用次数
    @Before("@within(org.springframework.stereotype.Service)")
    public void calculateInvokeTimes(JoinPoint joinPoint) {
        String methodSig=joinPoint.getSignature().toString();
        Integer base= invokeTimes.containsKey(methodSig)?invokeTimes.get(methodSig):0;
        invokeTimes.put(methodSig,base+1);
    }

    public Map<String, Integer> getErrorTimes(){return errorTimes;}

    public Map<String,Integer> getInvokeTimes(){return invokeTimes;}

    public Map<String,Long> getLongestTime(){return longestTime;}

    public Map<String,Long> getShortestTime(){return shortestTime;}

    public Map<String,Long> getAvgTime(){return avgTime;}
}
